/**
 * 	 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *   @author Colin Redmond
 *   Copyright (C) 2011 Colin Redmond
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.cred.industries.platform.business.facade;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import javax.ws.rs.core.Response.Status;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cred.industries.core.database.DBConnectionManager;
import com.cred.industries.core.exceptions.ApplicationException;
import com.cred.industries.core.transferObj.result.ReturnCodes;
import com.cred.industries.platform.business.objects.CustomerBO;
import com.cred.industries.platform.business.objects.CustomerRolesBO;
import com.cred.industries.platform.dao.CustomerDAOFactory;
import com.cred.industries.platform.dao.CustomerRolesDAOFactory;
import com.cred.industries.platform.dao.ICustomerDAO;
import com.cred.industries.platform.dao.ICustomerRolesDAO;
import com.cred.industries.platform.defines.Role;
import com.cred.industries.platform.session.SessionData;

/**
 * Facade for all interactions with the customer
 * 
 */
public class CustomerFacade {
	private final static Logger logger = LoggerFactory.getLogger(CustomerFacade.class);

	/**
	 * Creates a customer, along with a default role of user.
	 * @param custToCreate the BO object that holds all the information needed to create a customer
	 * @return the CustomerBO of the customer we created
	 * @throws IllegalArgumentException, ApplicationException
	 */
	public CustomerBO createCustomer(CustomerBO custToCreate) {
		
		try {
						
			if(custToCreate == null) {
				throw new IllegalArgumentException("Customer we are creating cant be null");
			}
		
			//make sure we are not using the customer id passed in
			custToCreate.setCustomerId(0);
			
			//populate the customer business object with the transfer object and validate
			if(!custToCreate.validateCustomer())	{
				throw new ApplicationException(Status.BAD_REQUEST, custToCreate.getValidationErrors(), "unable to validate customer " + custToCreate.getPersonaName());
			}
					
			// Setup the connection with the DB
			 try (Connection connect = DBConnectionManager.getInstance().getConnection()) {
				 
				try {
					//make sure we are not committing on every sql Query
					//since we want to use this as a transaction
					connect.setAutoCommit(false);
					
					//use the customer business object to insert a new customer in the Db with the DAO
					ICustomerDAO custDAO = CustomerDAOFactory.create();
					custDAO.insert(connect, custToCreate);
					
					//create a customer roles BO and give it the default user role. 
					CustomerRolesBO custRolesBO = new CustomerRolesBO(custToCreate.getPersonaName());
					custRolesBO.assignRole(Role.USER);
					
					//insert the custRolesBO into the Db
					ICustomerRolesDAO custRolesDAO = CustomerRolesDAOFactory.create();
					custRolesDAO.insert(connect, custRolesBO);
					
					//commit the transaction if all went well
					connect.commit();
					logger.info("CustomerFacade created customer " + custToCreate.getPersonaName() + " id: " + custToCreate.getCustomerId());
					
				} catch (Exception e) {
					connect.rollback();
					throw e; //we just want to roll back and let the other handler deal with the exceptions
				}
				 
			 }
			
		} catch (SQLException e) {
			//rollback the transaction if there was an exception
			throw new ApplicationException(e, ReturnCodes.UNKNOWN_ERROR, "unable to create " + custToCreate.getPersonaName());
		} 
		
		//return the CustomerTrans with the new customer Id
		return custToCreate;
	}
	
	/**
	 * Updates the customer in the DetailedCustomerTrans. This will not change the CustomerId 
	 * only an admin or CS is able to change the persona name
	 * @param custToUpdate contains all information needed to update a customer
	 * @return DetailedCustomerTrans that matches what is in the database
	 * @throws IllegalArgumentException, ApplicationException
	 */
	public CustomerBO updateCustomer(CustomerBO custToUpdate) {
		
		if(custToUpdate == null) {
			throw new IllegalArgumentException("Customer we are updating cant be null");
		}
	
		//get the currently authenticated user
		CustomerBO authenticatedUser = SessionData.getSessionCustomer();
		
		//get the customer we want to modify from the DAO
		CustomerBO custToModify = getCustomerById(custToUpdate.getCustomerId());
		
		//if the user isnt authenticated they are not authorized
		if(authenticatedUser == null) {
			throw new ApplicationException(Status.UNAUTHORIZED, ReturnCodes.UNKNOWN_ERROR, "no authorized user " + custToUpdate.getPersonaName());
		}
		
		//if we can not find the customer in the DB then return not found
		if(custToModify == null) {
			throw new ApplicationException(Status.NOT_FOUND, ReturnCodes.UNKNOWN_ERROR, "didnt find customer to update " + custToUpdate.getPersonaName());
		}
		
		boolean isAuthorizedToModify = custToModify.authorizedToModify();
		boolean isSuperUser = custToModify.authorizedAsSuperuser();
		boolean isSelf = custToModify.isAuthenticatedUser();
		
		//you are only allowed to modify your account unless you are an admin or CS
		if(!isAuthorizedToModify) {
			throw new ApplicationException(Status.UNAUTHORIZED, ReturnCodes.UNKNOWN_ERROR, "not authorized to modify " + custToUpdate.getPersonaName() + " by " + authenticatedUser.getPersonaName());
		}
		
		//only a super user for the BO can change the persona name
		if(isSuperUser) {
			custToModify.setPersonaName(custToUpdate.getPersonaName());
		}
		
		//create the customer BO we are going to use to modify the DB
		custToModify.setFirstName(custToUpdate.getFirstName());
		custToModify.setLastName(custToUpdate.getLastName());
		custToModify.setPassword(custToUpdate.getPassword());
		custToModify.setEmailAddress(custToUpdate.getEmailAddress());
		
		//validate the new customer data
		if(!custToModify.validateCustomer())	{
			throw new ApplicationException(ReturnCodes.UNKNOWN_ERROR, "unable to validate customer " + authenticatedUser.getPersonaName());
		}
		
		//use the customer business object to insert a new customer in the Db with the DAO
		ICustomerDAO custDAO = CustomerDAOFactory.create();
		custDAO.update(custToModify);
		
		//now that the user has changed update the session if 
		if(isSelf) {
			SessionData.setSessionCustomer(custToModify);
		}
		
		logger.info("CustomerFacade updated customer " + custToUpdate.getPersonaName() + " id: " + custToUpdate.getCustomerId());
		
		//return the CustomerTrans with the new customer Id
		return custToModify;
	}
	
	
	/**
	 * Used to get a customer by Id
	 * @param custId, the customerID of the customer we are looking for
	 * @return the customer if found
	 */
	public CustomerBO getCustomerById(int custId) {
		
		CustomerBO foundCust = null;
		
		//use the customer Data object to find the customer BO
		ICustomerDAO custDAO = CustomerDAOFactory.create();
		foundCust = custDAO.findCustomerById(custId);
		
		//use the customer BO to build the customer trans
		return foundCust;
	}
	
	/**
	 * Gets a single customer by matching persona names. This must be an exact match.
	 * @param personaName name of the customer we are looking for
	 * @return the customer if found
	 */
	public CustomerBO getCustomerByPersona(String personaName) {
		
		CustomerBO foundCust = null;
		
		//use the customer Data object to find the customer BO
		ICustomerDAO custDAO = CustomerDAOFactory.create();
		foundCust = custDAO.findCustomerByPersonaName(personaName);
		
		//use the customer BO to build the customer trans
		return foundCust;
	}
	
	/**
	 * Gets a single customer by matching persona names. This must be an exact match.
	 * @param personaName name of the customer we are looking for
	 * @return the customer if found
	 */
	public CustomerBO getCustomerByEmail(String emailAddress) {
		
		CustomerBO foundCust = null;
		
		//use the customer Data object to find the customer BO
		ICustomerDAO custDAO = CustomerDAOFactory.create();
		foundCust = custDAO.findCustomerByEmail(emailAddress);
		
		//use the customer BO to build the customer trans
		return foundCust;
	}
	
	/**
	 * Gets a set of customers that match the persona name
	 * You can specify where you want to start the set and how many you want up to 100
	 * 
	 * @param personaName partial name of the customer we are looking for
	 * @param setStart where to start getting the customers. Ie get the 25th customer 
	 * @param setSize how many customers to get
	 * @return set of customers that match the persona Name
	 */
	public List<CustomerBO> getCustomersByName(String personaName, int setStart, int setSize) {
		
		List<CustomerBO> foundCust = null;
		
		Math.min(setSize, 100);
		//use the customer Data object to find the customer BO
		ICustomerDAO custDAO = CustomerDAOFactory.create();
		foundCust = custDAO.findCustomersByPersonaName(personaName, setStart, setSize);
		
		//use the customer BO to build the customer trans
		return foundCust;
	}
}
